package cn.tworice.upload.client.minio;

import cn.tworice.common.util.HttpUtil;
import cn.tworice.common.util.StringUtils;
import cn.tworice.common.util.file.FileUtils;
import cn.tworice.common.util.spring.ApplicationUtil;
import cn.tworice.upload.client.FileClient;
import cn.tworice.upload.client.FileClientProperties;
import cn.tworice.upload.service.UploadUtil;
import io.minio.*;
import io.minio.errors.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.multipart.MultipartFile;

import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;

@Slf4j
public class MinioFileClient implements FileClient {

    private final MinioClient client;

    private final UploadUtil uploadUtil;

    private final FileClientProperties properties;

    public MinioFileClient() {
        uploadUtil = ApplicationUtil.getApplicationContext().getBean(UploadUtil.class);
        properties = ApplicationUtil.getApplicationContext().getBean(FileClientProperties.class);
        // 初始化客户端
        client = MinioClient.builder()
                .endpoint(buildEndpointURL()) // Endpoint URL
                .credentials(properties.getAccessKey(), properties.getSecretKey()) // 认证密钥
                .build();
    }


    @Override
    public String upload(MultipartFile file) throws ServerException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
        return this.uploadToBucket(file, properties.getBucket());
    }

    /**
     * TODO 待实现
     * @param image
     * @return
     * @throws IOException
     */
    @Override
    public String upload(BufferedImage image) throws IOException {
        return "";
    }

    @Override
    public String upload(byte[] fileBytes, String fileExt) throws IOException, ServerException, InsufficientDataException, ErrorResponseException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
        return this.uploadToBucket(fileBytes, fileExt, properties.getBucket());
    }

    /**
     * 上传文件并指定文件名
     * @param fileBytes 文件字节数组
     * @param fileName 文件名
     * @return 文件名
     */
    @Override
    public String uploadToName(byte[] fileBytes, String fileName) throws IOException, ServerException, InsufficientDataException, ErrorResponseException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
        InputStream inputStream = new ByteArrayInputStream(fileBytes);
        String contentType = getContentType(uploadUtil.getFileExt(fileName).substring(1));

        client.putObject(
                PutObjectArgs.builder()
                        .bucket(properties.getBucket())
                        .object(fileName)
                        .stream(inputStream, fileBytes.length, -1)
                        .contentType(contentType)
                        .build());

        return fileName;
    }

    @Override
    public String uploadToPath(MultipartFile file, String bucketName) throws ServerException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
        return this.uploadToBucket(file, properties.getBucket());
    }

    /**
     * 删除文件
     * @param path 相对路径
     */
    @Override
    public void delete(String path) throws ServerException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
        // 校验存储桶是否存在
        verifyBucket(properties.getBucket());

        // 删除文件
        client.removeObject(
                RemoveObjectArgs.builder()
                        .bucket(properties.getBucket())
                        .object(path)
                        .build()
        );
        log.info("File {} deleted successfully from bucket {}", path, properties.getBucket());
    }

    /**
     * 获取文件
     * @param path 相对路径
     * @return
     * @throws Exception
     */
    @Override
    public byte[] getContent(String path) throws Exception {
        try (InputStream inputStream = client.getObject(
                GetObjectArgs.builder()
                        .bucket(properties.getBucket())
                        .object(path)
                        .build())) {

            // 校验文件内容是否为空
            if (inputStream == null) {
                log.warn("File content is empty or not found for {}", path);
                return new byte[0];  // 返回一个空字节数组
            }

            // 使用缓冲区读取文件内容
            byte[] buffer = new byte[8192];  // 设置缓冲区大小
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            int bytesRead;

            while ((bytesRead = inputStream.read(buffer)) != -1) {
                byteArrayOutputStream.write(buffer, 0, bytesRead);
            }

            byte[] content = byteArrayOutputStream.toByteArray();
            log.info("File content retrieved successfully for {}", path);
            return content;
        } catch (ErrorResponseException | ServerException | IOException e) {
            log.error("Error retrieving file content for {} from bucket {}: {}", path, properties.getBucket(), e.getMessage());
            throw e;  // 重新抛出异常
        } catch (Exception e) {
            log.error("Unexpected error occurred while retrieving file content for {} from bucket {}: {}", path, properties.getBucket(), e.getMessage());
            throw new RuntimeException("Unexpected error occurred during file retrieval", e);  // 抛出包装的异常
        }
    }

    /**
     * 基于 endpoint 构建调用云服务的 URL 地址
     *
     * @return URI 地址
     */
    private String buildEndpointURL() {
        // 如果已经是 http 或者 https，则不进行拼接.主要适配 MinIO
        if (HttpUtil.isHttp(properties.getEndpoint()) || HttpUtil.isHttps(properties.getEndpoint())) {
            return properties.getEndpoint();
        }
        return String.format("https://%s", properties.getEndpoint());
    }

    /**
     * 校验存储桶是否存在，不存在则创建
     * @param bucketName 存储桶名称
     */
    private void verifyBucket(String bucketName) throws ServerException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
        // 检查存储桶是否存在
        boolean isExist = client.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
        if (!isExist) {
            client.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
        }
    }

    /**
     * 上传文件到指定存储桶
     * @param file 文件
     * @param bucketName 存储桶名称
     * @return 文件名称
     */
    private String uploadToBucket(MultipartFile file, String bucketName) throws ServerException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
        this.verifyBucket(bucketName);
        String fileExt = uploadUtil.getFileExt(file);
        String fileName = StringUtils.generateUuid() + fileExt;
        // 上传文件到MinIO
        client.putObject(
                PutObjectArgs.builder()
                        .bucket(bucketName)
                        .object(fileName)
                        .stream(file.getInputStream(), file.getSize(), -1)
                        .contentType(getContentType(fileExt.substring(1))) // 不包含.
                        .build()
        );
        return fileName;
    }

    /**
     * 上传文件到指定存储桶
     * @param fileBytes 文件字节数组
     * @param fileExt 文件扩展名
     * @param bucketName 存储桶
     * @return 文件名称
     */
    private String uploadToBucket(byte[] fileBytes, String fileExt, String bucketName) throws ServerException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
        String fileName = StringUtils.generateUuid() + "." + fileExt;
        InputStream inputStream = new ByteArrayInputStream(fileBytes);
        String contentType = getContentType(fileExt);

        client.putObject(
                PutObjectArgs.builder()
                        .bucket(bucketName)
                        .object(fileName)
                        .stream(inputStream, fileBytes.length, -1)
                        .contentType(contentType)
                        .build());

        return fileName;
    }

    private String getContentType(String fileExt) {
        String ext = fileExt.toLowerCase().replace(".", "");
        switch (ext) {
            case "jpg":
            case "jpeg": return "image/jpeg";
            case "png":  return "image/png";
            case "gif":  return "image/gif";
            case "txt":   return "text/plain";
            case "pdf":   return "application/pdf";
            case "doc":  return "application/msword";
            case "docx": return "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
            case "xls":  return "application/vnd.ms-excel";
            case "xlsx": return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
            case "zip":  return "application/zip";
            default:     return "application/octet-stream";
        }
    }
}
